package websocket

import (
	"context"
	"sync"
	"time"

	"dev.ixmxm.top/rms/codes"
	"dev.ixmxm.top/rms/utils/log"
	"github.com/gin-gonic/gin"
)

func NewServer() *Pusher {
	ctx, cancel := context.WithCancel(context.Background())
	wp := &Pusher{sessions: make(map[string]Session, 0), cancel: cancel}
	wp.startCheckSession(ctx)
	return wp
}

type Pusher struct {
	sessions     map[string]Session
	cancel       context.CancelFunc
	sessionMutex sync.RWMutex
}

func (wp *Pusher) AddSession(c *gin.Context, session Session) codes.Code {
	if !c.IsWebsocket() {
		log.ErrorWithContext(c, "connection is not websockets")
		return codes.ErrWebsockets
	}

	wp.sessionMutex.Lock()
	defer wp.sessionMutex.Unlock()
	if _, found := wp.sessions[session.RemoteAddr()]; found {
		return codes.ErrWebsockets
	}
	// log.InfoWithContext(c, "add new session:", session.RemoteAddr())
	wp.sessions[session.RemoteAddr()] = session
	// log.InfofWithContext(c, "websockets sessions number:%d", len(wp.sessions))
	return nil
}

func (wp *Pusher) Push(data interface{}) {
	wp.sessionMutex.Lock()
	defer wp.sessionMutex.Unlock()
	for _, u := range wp.sessions {
		u.SendJson(data)
	}
}

func (wp *Pusher) Close() {
	wp.sessionMutex.Lock()
	defer wp.sessionMutex.Unlock()
	for _, session := range wp.sessions {
		session.Close()
	}
	wp.cancel()
}

func (wp *Pusher) startCheckSession(ctx context.Context) {
	check := func() {
		wp.sessionMutex.Lock()
		defer wp.sessionMutex.Unlock()
		for _, session := range wp.sessions {
			if !session.IsOK() {
				// log.Info("remove session:", session.RemoteAddr())
				delete(wp.sessions, session.RemoteAddr())
			}
		}
		//todo
	}
	go func() {
		tick := time.NewTicker(3 * time.Second)
		defer tick.Stop()
		for {
			select {
			case <-tick.C:
				check()
			case <-ctx.Done():
				check()
				return
			}
		}
	}()
}
